﻿#region Copyright
/// <copyright>
/// Copyright (c) 2008 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;

namespace Osh
{
	public abstract class WorkerBase : IWorker
	{
		private string _name;
		private Thread _workerThread;
		private ManualResetEvent _stopEvent = new ManualResetEvent(false);
		private TimeSpan _stopWaitTimeout = TimeSpan.FromMilliseconds(1000);
		private TimeSpan _onErrorSleepTimeout = TimeSpan.Zero;

		public WorkerBase(string name)
		{
			_name = name;
		}

		public WorkerBase(string name, TimeSpan stopWaitTimeout, TimeSpan onErrorSleepTimeout)
		{
			_name = name;
			_stopWaitTimeout = stopWaitTimeout;
			_onErrorSleepTimeout = onErrorSleepTimeout;
		}

		#region IWorker Members
		public virtual string Name
		{
			get { return _name; }
			set { _name = value; }
		}

		public virtual bool IsWorking
		{
			get { return _workerThread != null; }
		}

		public virtual void Start()
		{
			if(this.IsWorking)
				return;

			_stopEvent.Reset();

			_workerThread = new Thread(new ThreadStart(ThreadProc));
			_workerThread.IsBackground = true;
			_workerThread.Priority = ThreadPriority.BelowNormal;
			_workerThread.Start();
		}

		public virtual void Stop()
		{
			Thread workerThread = _workerThread;
			if(workerThread == null)
				return;

			_stopEvent.Set();
			Wait(workerThread);
		}
		#endregion

		protected ManualResetEvent StopEvent
		{
			get { return _stopEvent; }
		}

		protected virtual void Wait(Thread workerThread)
		{
			if(workerThread == null)
				return;

			if(!workerThread.Join(_stopWaitTimeout))
			{
				if(OnWaitTimeout())
					workerThread.Join();
				else
				{
					workerThread.Abort();
					workerThread.Join();
				}
			}
		}

		/// <summary>
		/// Method called when worker was asked to stop, but after StopWaitTimeout it's still working.
		/// Returns true to continue wait operation, otherwise returns false.
		/// </summary>
		/// <returns></returns>
		protected virtual bool OnWaitTimeout()
		{
			return true;
		}

		protected virtual TimeSpan StopWaitTimeout
		{
			get { return _stopWaitTimeout; }
			set { _stopWaitTimeout = value; }
		}

		protected virtual TimeSpan OnErrorSleepTimeout
		{
			get { return _onErrorSleepTimeout; }
			set { _onErrorSleepTimeout = value; }
		}

		protected virtual void ThreadProc()
		{
			try
			{
				while(!_stopEvent.WaitOne(0, false))
				{
					try
					{
						DoWork();
					}
					catch(OutOfMemoryException) { break; }
					catch(ThreadAbortException) { break; }
					catch(Exception e)
					{
						OnError(e);
						_stopEvent.WaitOne(_onErrorSleepTimeout, false);
					}
				}
			}
			finally
			{
				_workerThread = null;
			}
		}

		/// <summary>
		/// The main method to perform work.
		/// If method throws an exception it will be passed to Error event and after OnErrorSleepTimeout the method will called again.
		/// </summary>
		protected abstract void DoWork();

		protected virtual void OnError(Exception exception)
		{
			if(this.Error != null)
			{
				try
				{
					this.Error(this, new ErrorEventArgs(exception));
				}
				catch { }
			}
		}

		public event EventHandler<ErrorEventArgs> Error;
	}
}
